using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Collections;

namespace haxeProject {

	public class ProjectOutput {

		public static String FLASH = "swf";
		public static String FLASH9 = "swf9";
		public static String JS = "js";
		public static String NEKO = "neko";
		public static String PHP = "php";

		public String name;
		public String mode;
		public String output;
		public String library;
		public String mainClass;
		public String extra;
		public String command;
		public bool mainCall;
		public bool debug;
		public bool active;

		public ProjectOutput( String name ){
			this.name = name;
			this.mode = FLASH;
			this.output = "index.swf";
			mainClass = "";
			library = "";
			command = "";
			mainCall = false;
			debug = false;
			active = true;
			extra = "";
		}

		public void Validate() {
			if( mode != FLASH && mode != FLASH9 )
				library = "";
		}

		public String Extension() {
			if( mode  == FLASH || mode == FLASH9 )
				return ".swf";
			if( mode == JS )
				return ".js";
			if( mode == PHP )
				return "";
			return ".n";
		}
	}

	public class ProjectFile : IComparable {
		public String path;
		public bool isFolder;
		public ProjectFile parent;
		public ArrayList childs;
		public bool expanded;

		public ProjectFile( String p, bool f ) {
			path = p;
			isFolder = f;
			if( isFolder )
				childs = new ArrayList();
		}

		public void Add( ProjectFile f ) {
			childs.Add(f);			
			f.parent = this;
		}

		public void Remove( ProjectFile f ) {
			childs.Remove(f);
			f.parent = null;
		}
		
		public void Sort() {
			childs.Sort();
		}
		
		public int CompareTo( Object o ) {
			ProjectFile f = o as ProjectFile;
			if( isFolder != f.isFolder )
				return isFolder?-1:1;
			return ToString().CompareTo(f.ToString());
		}
		
		public override String ToString() {
			return Path.GetFileName(path);
		}

	}

	public class ProjectData {

		public String file;
		public int selectedOutput;
		public ArrayList outputs;
		public ProjectFile root;

		private XmlTextWriter w;

		public ProjectData( String file, bool create ) {
			this.file = Path.GetFullPath(file);
			outputs = new ArrayList();
			root = new ProjectFile("/",true);
			root.expanded = true;
			selectedOutput = 0;
			if( create )
				save();
			else
				load();
		}

		public String buildHXML() {
			String hxml = Path.GetDirectoryName(file) + "/" + Path.GetFileNameWithoutExtension(file) + ".hxml";
			bool first = true;
			StreamWriter f = File.CreateText(hxml);
			try {			
			foreach( ProjectOutput p in outputs ) {
				p.Validate();
				if( !p.active )
					continue;
				if( first )
					first = false;
				else
					f.Write("\n--next\n");
				f.WriteLine("# "+p.name);
				f.WriteLine("-"+p.mode+" "+p.output);
				if( p.library != "" )
					f.WriteLine("-swf-lib "+p.library);
				if( p.mainCall )
					f.Write("-main ");
				if( p.mainClass == "" )
					error("Output '"+p.name+"' does not have an Entry Class defined");
				f.WriteLine(p.mainClass);
				if( p.debug )
					f.WriteLine("-debug");

				f.WriteLine(p.extra);
			}
			} finally {
				f.Close();
			}
			return hxml;
		}
		
		public ProjectFile GetSubFile( String path, ProjectFile parent ) {
			if( parent.path == path )
				return parent;
			if( parent.isFolder )
				foreach( ProjectFile f in parent.childs ) {
					ProjectFile c = GetSubFile(path,f);
					if( c != null )
						return c;
				}
			return null;
		}
		

		private void error( String msg ) {
			throw new Exception(msg);
		}
		
		#region xml load/save

		private XmlElement Pick( XmlElement x, String name ) {
			String s = "";
			foreach( XmlElement c in x ) {
				if( c.NodeType == XmlNodeType.Element && c.Name == name )
					return c;
			    s += c.Name;
			}
			error("Missing node "+name+"("+s);
			return null;
		}

		private bool hasAttrib( XmlElement x, String name ) {
			return x.Attributes.GetNamedItem(name) != null;
		}
		
		private String attrib( XmlElement x, String name ) {
			String v = x.Attributes.GetNamedItem(name).InnerText;
			if( v == null )
				error("Missing attribute "+name+" in "+x.Name);
			return v;
		}

		private void load() {
			XmlDocument x = new XmlDocument();
			x.Load(file);
			loadHaxe(x.DocumentElement);
		}

		private void loadHaxe( XmlElement x ) {
			selectedOutput = int.Parse(attrib(x,"selected"));
			foreach( XmlElement c in x )
				switch( c.Name ) {
				case "output":
					loadOutput(c);
					break;
				case "files":
					root = loadFile(c);
					root.expanded = true;
					break;
				default:
					error("Unknown node "+c.Name);
					break;
				}

		}

		private ProjectFile loadFile( XmlElement x ) {
			ProjectFile f = new ProjectFile(attrib(x,"path"),x.Name != "file");
			if( f.isFolder )
				foreach( XmlElement c in x )
					if( c.Name == "file" || c.Name == "dir" )
						f.Add(loadFile(c));
			return f;
		}

		private void loadOutput( XmlElement x ) {
			ProjectOutput p = new ProjectOutput(attrib(x,"name"));
			p.mode = attrib(x,"mode");
			p.output = attrib(x,"out");
			p.library = attrib(x,"lib");
			p.mainClass = attrib(x,"class");
			p.command = attrib(x,"cmd");
			p.mainCall = Boolean.Parse(attrib(x,"main"));
			p.debug = Boolean.Parse(attrib(x,"debug"));
			if( hasAttrib(x,"active") )
				p.active = Boolean.Parse(attrib(x,"active"));	
			p.extra = x.InnerText;
			outputs.Add(p);
		}

		public void save() {
			buildHXML();
			w = new XmlTextWriter(file,Encoding.UTF8);
			w.Formatting = Formatting.Indented;
			w.WriteStartElement("haxe");
			w.WriteAttributeString("selected",selectedOutput.ToString());
			saveOutputs();
			saveFile(root);
			w.WriteEndElement();
			w.Close();
		}

		private void saveOutputs() {
			foreach( ProjectOutput p in outputs ) {
				p.Validate();
				w.WriteStartElement("output");
				w.WriteAttributeString("name",p.name);
				w.WriteAttributeString("mode",p.mode);
				w.WriteAttributeString("out",p.output);
				w.WriteAttributeString("class",p.mainClass);
				w.WriteAttributeString("lib",p.library);
				w.WriteAttributeString("cmd",p.command);
				w.WriteAttributeString("main",p.mainCall.ToString());
				w.WriteAttributeString("debug",p.debug.ToString());
				if( !p.active )
					w.WriteAttributeString("active",p.active.ToString());
				w.WriteRaw(p.extra);
				w.WriteEndElement();
			}
		}

		private void saveFile( ProjectFile f ) {
			w.WriteStartElement(f.isFolder ? ((f == root)?"files":"dir") : "file" );
			w.WriteAttributeString("path",f.path);
			if( f.isFolder )
				foreach( ProjectFile c in f.childs )
					saveFile(c);
			w.WriteEndElement();
		}
		
		#endregion

	}
}
